
#include "StdAfx.h"
#include "HttpDownload.h"

#ifndef DEF_VC6

#include <NetLibs.h>

HttpDownload::HttpDownload(){	
	handle_ = 0;
	//curl_global_init( CURL_GLOBAL_ALL );
}

HttpDownload::~HttpDownload(){
}

size_t HttpDownload::callback_write_download( char* buffer, size_t size, size_t nitems, void* userp ){ 
	char* newbuff = 0;
    size_t rembuff = 0;
	URL_FILE *url = (URL_FILE *)userp;
	size *= nitems;
 
	rembuff=url->buffer_len - url->buffer_pos; /* remaining space in buffer */ 
 
	if(size > rembuff) {
		/* not enough space in buffer */ 
		newbuff = (char*)realloc(url->buffer,url->buffer_len + (size - rembuff));
		if(newbuff==0) {
			fprintf(stderr,"callback buffer grow failed\n");
			size=rembuff;
		}
		else{
			/* realloc suceeded increase buffer size*/ 
			url->buffer_len+=size - rembuff;
			url->buffer=newbuff;
		}
	}
 
	memcpy(&url->buffer[url->buffer_pos], buffer, size);
	url->buffer_pos += size;
 
	return size;
}

int HttpDownload::download( char* url, char* file ){		
	URL_FILE *handle = 0;
	FILE *outf = 0;
 
	int nread = 0;
	char buffer[256];	
	memset( buffer, 0, sizeof( char ) * 256 );
 
	/* copy from url line by line with fgets */ 
	outf=fopen("fgets.test","w+");
	if(!outf) {
		perror("couldn't open fgets output file\n");
		return 1;
	}
 
	handle = url_fopen(url, "r");
	if(!handle) {
		printf("couldn't url_fopen() %s\n", url);
		fclose(outf);
		return 2;
	}
 
	while(!url_feof(handle)) {
		url_fgets(buffer,sizeof(buffer),handle);
		fwrite(buffer,1,strlen(buffer),outf);
	}
 
	url_fclose(handle);
 
	fclose(outf); 
 
	/* Copy from url with fread */ 
	outf=fopen("fread.test","w+");
	if(!outf) {
		perror("couldn't open fread output file\n");
		return 1;
	}
 
	handle = url_fopen("testfile", "r");
	if(!handle) {
		printf("couldn't url_fopen() testfile\n");
		fclose(outf);
		return 2;
	}
 
	do {
		nread = url_fread(buffer, 1,sizeof(buffer), handle);
		fwrite(buffer,1,nread,outf);
	} while(nread);
 
	url_fclose(handle);
 
	fclose(outf);
 
	/* Test rewind */ 
	outf=fopen("rewind.test","w+");
	if(!outf) {
		perror("couldn't open fread output file\n");
		return 1;
	}
 
	handle = url_fopen("testfile", "r");
	if(!handle) {
		printf("couldn't url_fopen() testfile\n");
		fclose(outf);
		return 2;
	}
 
	nread = url_fread(buffer, 1,sizeof(buffer), handle);
	fwrite(buffer,1,nread,outf);
	url_rewind(handle);
 
	buffer[0]='\n';
	fwrite(buffer,1,1,outf);
 
	nread = url_fread(buffer, 1,sizeof(buffer), handle);
	fwrite(buffer,1,nread,outf); 
 
	url_fclose(handle);
 
	fclose(outf);

	return 0;
}

HttpDownload::URL_FILE* HttpDownload::url_fopen( const char *url,const char *operation ){
	/* this code could check for URLs or types in the 'url' and
     basicly use the real fopen() for standard files */ 
 
	URL_FILE* file = 0;
	(void)operation;
 
	file = ( HttpDownload::URL_FILE* )malloc(sizeof(URL_FILE));
	if(!file)
		return 0;
 
	memset(file, 0, sizeof(URL_FILE));
 
	if((file->handle.file=fopen(url,operation)))
		file->type = CFTYPE_FILE; /* marked as URL */  
	else{
		file->type = CFTYPE_CURL; /* marked as URL */ 
		file->handle.curl = curl_easy_init();
 
		curl_easy_setopt(file->handle.curl, CURLOPT_URL, url);
		curl_easy_setopt(file->handle.curl, CURLOPT_WRITEDATA, file);
		curl_easy_setopt(file->handle.curl, CURLOPT_VERBOSE, 1L);
		curl_easy_setopt(file->handle.curl, CURLOPT_WRITEFUNCTION, callback_write_download );
 
		if(! handle_ )
			handle_ = curl_multi_init();
 
		curl_multi_add_handle( handle_, file->handle.curl);
 
		/* lets start the fetch */ 
		curl_multi_perform( handle_, &file->still_running);
 
		if((file->buffer_pos == 0) && (!file->still_running)) {
			/* if still_running is 0 now, we should return NULL */ 
 
			/* make sure the easy handle is not in the multi handle anymore */ 
			curl_multi_remove_handle( handle_, file->handle.curl);
 
			/* cleanup */ 
			curl_easy_cleanup(file->handle.curl);
 
			free(file);
 
			file = 0;
		}
	}
	
	return file;
}

int HttpDownload::url_fclose( URL_FILE* file ){
	int ret = 0;/* default is good return */ 
 
	switch( file->type ) {
		case CFTYPE_FILE:
			ret=fclose(file->handle.file); /* passthrough */ 
		break;
 
		case CFTYPE_CURL:
			/* make sure the easy handle is not in the multi handle anymore */ 
			curl_multi_remove_handle( handle_, file->handle.curl);
 
			/* cleanup */ 
			curl_easy_cleanup(file->handle.curl);
		break;
 
		default: /* unknown or supported type - oh dear */ 
			ret=EOF;
			errno= EBADF;
		break;
	}
 
	if( file->buffer )
		free(file->buffer);/* free any allocated buffer space */ 
 
	free( file );
 
	return ret;
}

int HttpDownload::url_feof( URL_FILE* file ){
	int ret = 0;
 
	switch(file->type) {
		case CFTYPE_FILE:
			ret=feof(file->handle.file);
		break;
 
		case CFTYPE_CURL:
			if((file->buffer_pos == 0) && (!file->still_running))
				ret = 1;
		break;
 
		default: /* unknown or supported type - oh dear */ 
			ret=-1;
			errno = EBADF;
		break;
	}

	return ret;
}

size_t HttpDownload::url_fread( void* ptr, size_t size, size_t nmemb, URL_FILE* file ){
	size_t want = 0;
 
	switch(file->type) {
		case CFTYPE_FILE:
			want=fread(ptr,size,nmemb,file->handle.file);
		break;
 
		case CFTYPE_CURL:
			want = nmemb * size;
 
			fill_buffer(file,want);
 
			/* check if theres data in the buffer - if not fill_buffer()
			* either errored or EOF */ 
			if(!file->buffer_pos)
				return 0;
 
			/* ensure only available data is considered */ 
			if(file->buffer_pos < want)
				want = file->buffer_pos;
 
			/* xfer data to caller */ 
			memcpy(ptr, file->buffer, want);
 
			use_buffer(file,want);
 
			want = want / size;     /* number of items */ 
		break;
 
		default: /* unknown or supported type - oh dear */ 
			want=0;
			errno = EBADF;
		break;
 
	}
	
	return want;
}

char* HttpDownload::url_fgets( char* ptr, size_t size, URL_FILE* file ){
	size_t want = size - 1;/* always need to leave room for zero termination */ 
	size_t loop = 0;
 
	switch(file->type) {
		case CFTYPE_FILE:
			ptr = fgets(ptr,size,file->handle.file);
		break;
 
		case CFTYPE_CURL:
			fill_buffer(file,want);
 
			/* check if theres data in the buffer - if not fill either errored or
			* EOF */ 
			if(!file->buffer_pos)
				return 0;
 
			/* ensure only available data is considered */ 
			if(file->buffer_pos < want)
				want = file->buffer_pos;
 
			/*buffer contains data */ 
			/* look for newline or eof */ 
			for(loop=0;loop < want;loop++) {
				if(file->buffer[loop] == '\n') {
					want=loop+1;/* include newline */ 
					break;
				}
			}
 
			/* xfer data to caller */ 
			memcpy(ptr, file->buffer, want);
			ptr[want]=0;/* allways null terminate */ 
 
			use_buffer(file,want);
 
		break;
 
		default: /* unknown or supported type - oh dear */ 
			ptr=0;
			errno = EBADF;
		break;
	}
 
	return ptr;/*success */ 
}

void HttpDownload::url_rewind( URL_FILE* file ){
	switch(file->type) {
		case CFTYPE_FILE:
			rewind(file->handle.file); /* passthrough */ 
		break;
 
		case CFTYPE_CURL:
			/* halt transaction */ 
			curl_multi_remove_handle( handle_, file->handle.curl);
 
			/* restart */ 
			curl_multi_add_handle( handle_, file->handle.curl);
 
			/* ditch buffer - write will recreate - resets stream pos*/ 
			if(file->buffer)
				free(file->buffer);
 
			file->buffer=0;
			file->buffer_pos=0;
			file->buffer_len=0;
 
		break;
 
		default: /* unknown or supported type - oh dear */ 
			break;
	}
}

int HttpDownload::fill_buffer( HttpDownload::URL_FILE* file, size_t want ){
	fd_set fdread;
	fd_set fdwrite;
	fd_set fdexcep;
	struct timeval timeout;
	int rc = 0;
 
	/* only attempt to fill buffer if transactions still running and buffer
	* doesnt exceed required size already
	*/ 
	if( (! file->still_running ) || ( file->buffer_pos > want ) )
		return 0;
 
	/* attempt to fill buffer */ 
	do{
		int maxfd = -1;
		long curl_timeo = -1;
 
		FD_ZERO(&fdread);
		FD_ZERO(&fdwrite);
		FD_ZERO(&fdexcep);
 
		/* set a suitable timeout to fail on */ 
		timeout.tv_sec = 60; /* 1 minute */ 
		timeout.tv_usec = 0;
 
		curl_multi_timeout( handle_, &curl_timeo );
		if( curl_timeo >= 0 ){
			timeout.tv_sec = curl_timeo / 1000;
			
			if(timeout.tv_sec > 1)
				timeout.tv_sec = 1;
			else
				timeout.tv_usec = (curl_timeo % 1000) * 1000;
		}
 
		/* get file descriptors from the transfers */ 
		CURLMcode mCode = curl_multi_fdset( handle_, &fdread, &fdwrite, &fdexcep, &maxfd);		
 
		/* In a real-world program you OF COURSE check the return code of the
		   function calls.  On success, the value of maxfd is guaranteed to be
		   greater or equal than -1.  We call select(maxfd + 1, ...), specially
		   in case of (maxfd == -1), we call select(0, ...), which is basically
		   equal to sleep. */ 
 
		rc = select(maxfd+1, &fdread, &fdwrite, &fdexcep, &timeout);
 
		switch(rc) {
			case -1:		
				print_network_error( __FUNCTION__, __LINE__ );
				return -10;
			break;
 
			case 0:
			default:
			/* timeout or readable/writable sockets */ 
				curl_multi_perform( handle_, &file->still_running);
			break;
		}
	}while( file->still_running && ( file->buffer_pos < want ) );
	
	return 1;
}

/* use to remove want bytes from the front of a files buffer */ 
int HttpDownload::use_buffer( HttpDownload::URL_FILE* file, int want ){
	/* sort out buffer */ 
	if( (file->buffer_pos - want) <= 0 ) {
		/* ditch buffer - write will recreate */ 
		if( file->buffer )
			free( file->buffer );
 
		file->buffer = 0;
		file->buffer_pos = 0;
		file->buffer_len = 0;
	}
	else{
		/* move rest down make it available for later */ 
		memmove( file->buffer, &file->buffer[want], (file->buffer_pos - want) ); 
		file->buffer_pos -= want;
	}
	
	return 0;
}

#endif
